home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 011 / reformat.arc / REFORMAT.PAS < prev   
Pascal/Delphi Source File  |  1986-05-20  |  47KB  |  1,380 lines

  1. program reformat;
  2. {
  3. Program to reformat any disk attached to a Olivetti PC or compatible.
  4. The program will probably work well on any MS/PC-DOS machine running under
  5. DOS 2.xx.  Fixed disks of all sizes
  6.  
  7. **    Modified May 16, 1986 By Rick Watson
  8. **
  9. **          Original program did not know about long (16 bit) FAT's.
  10. **          Therefore the program blew up with disks with over 4K
  11. **          clusters.  Made changes necessary to accomodate disks
  12. **          up to 16K clusters.
  13. **
  14. **          Also found that if the program is run on the default disk,
  15. **          a directory displayed garbage upon completion and DOS could
  16. **          not find any files.  This is because the program rearranged
  17. **          the FAT's and directories without telling DOS.  DOS keeps
  18. **          information in memory about the disk that no longer matches
  19. **          reality.  This can cause a great deal of tension when the
  20. **          user comes to the conclusion that his disk has been trashed.
  21. **          I have changed the program to require the user to reboot
  22. **          the system upon completion if it is run on the default drive.
  23. **
  24. **          I have tested this program with my 20 Meg hard
  25. **          disk, a 2 Meg RAMdisk and 360K floppies.  All testing seems
  26. **          to be successful, however...
  27. **          Since this program rewrites the FAT's, directories,
  28. **          sub-directories, and file data, it constitutes a risk.
  29. **          An undetected program bug, power interruption during use,
  30. **          a well directed cosmic ray, etc., etc., etc. could cause
  31. **          total and irreversible loss of ALL data on the disk being
  32. **          reformatted (The Norton Utilities will just laugh at you).
  33. **
  34. **                        USE AT YOUR OWN RISK!
  35. **                       (being backed up helps)
  36. **
  37. Global types                                                           }
  38.  
  39. type
  40.  
  41.   Regpack    = record case integer of
  42.                1: (ax, bx, cx, dx, bp, si, di, ds, es, flags : integer);
  43.                2: (al, ah, bl, bh, cl, ch, dl, dh            : byte);
  44.                end;
  45.  
  46.   Boot       = record
  47.                  Jump:                  array[0..2] of byte;
  48.                  OEM :                  array[0..7] of char;
  49.                  SectorSize:            integer;
  50.                  Clustersize:           byte;
  51.                  ReservedSectors:       integer;
  52.                  NumberOfFats:          byte;
  53.                  RootDirSize,
  54.                  TotalSectors:          integer;
  55.                  MediaDescriptor:       byte;
  56.                  FatSize,
  57.                  TrackSize,
  58.                  NumberOfHeads,
  59.                  NumberOfHiddenSectors: integer;
  60.                end;
  61.  
  62.   IntArray   = array[0..32766] of integer;
  63.  
  64.   Buffer     = array[0..32766] of byte;
  65.  
  66.   LongInteger      = array[0..1] of integer;
  67.  
  68.   DirectoryPointer = ^DirectoryEntry;
  69.  
  70.   DirectoryEntry   = record
  71.                        EntryName:          array[0..10] of char;
  72.                        Attribute:          byte;
  73.                        Reserved:           array[1..10] of byte;
  74.                        TimeLastUpdated:    integer;
  75.                        DateLastUpdated:    integer;
  76.                        StartingCluster:    integer;
  77.                        Filesize:           LongInteger;
  78.                        NewStartingCluster: integer;
  79.                        Next,
  80.                        SubDirectory:       DirectoryPointer;
  81.                      end;
  82.  
  83.   WorkString       = string[255];
  84.  
  85. const
  86.  
  87.   ReadOnly:        byte = $01;
  88.   HiddenFile:      byte = $02;
  89.   SystemFile:      byte = $04;
  90.   VolumeLabel:     byte = $08;
  91.   Subdirectory:    byte = $10;
  92.   Archive:         byte = $20;
  93.  
  94.   NeverUsed:       byte = $00;
  95.   Erased:          byte = $E5;
  96.  
  97.   FixedDisk:       byte = $F8;
  98.   Dual8Sector:     byte = $FF;
  99.   Single8Sector:   byte = $FE;
  100.   Dual9Sector:     byte = $FD;
  101.   Single9Sector:   byte = $FC;
  102.  
  103.   Unused:               integer = $0000;
  104.  
  105. var
  106.  
  107. { Drive characteristics and constants communications block             }
  108.  
  109.   DriveLetter:          char;
  110.   NumberOfFats,
  111.   Media,
  112.   DefaultDrive,
  113.   DriveNumber:          byte;
  114.   FreeClusters,
  115.   TotalDataClusters,
  116.   FirstDataSector,
  117.   FATsize,
  118.   FirstFATsector,
  119.   RootDirSize,
  120.   DirectorySectors,
  121.   FirstDirectorySector,
  122.   SectorSize,
  123.   ReservedMinimum,
  124.   ReservedMaximum,
  125.   BadCluster,
  126.   LastMinimum,
  127.   LastMaximum,
  128.   LastNormal,
  129.   ClusterSize:          integer;
  130.  
  131. { Global variables                                                     }
  132.  
  133.   Registers:            Regpack;
  134.   OldFATindex,
  135.   NewFATindex,
  136.   Errors,
  137.   LostClusters,
  138.   TotalFiles,
  139.   HiddenFiles,
  140.   InRootDirectory,
  141.   InSubdirectories,
  142.   NonContiguousFiles,
  143.   Subdirectories,
  144.   MovedClusters,
  145.   ClustersToMove,
  146.   Count:                integer;
  147.   SAVEaddress,
  148.   DTAddress:           ^Buffer;
  149.   PermutationAddress,
  150.   NewFATAddress,
  151.   OldFATAddress:       ^IntArray;
  152.   RootDir:              DirectoryPointer;
  153.   MovedField,
  154.   InputField,
  155.   LogField,
  156.   WarningField,
  157.   ErrorField,
  158.   DisasterField:        LongInteger;
  159.   Anything,
  160.   Instr:                char;
  161.   BigFAT,
  162.   NeedReboot,
  163.   AlreadyWritten:       boolean;
  164.   DiskLabel:            array[0..10] of char;
  165.  
  166. (* procedure Int25(var Registers: Regpack); external 'Int25.com'; *)
  167.  
  168. (* procedure Int26(var Registers: Regpack); external 'Int26.com'; *)
  169.  
  170. {$I REFORMAT.INC}
  171.  
  172. procedure Beep;
  173.  
  174. begin
  175.    write(chr(7));
  176. end;
  177.  
  178. procedure WriteLog(Text: WorkString);
  179.  
  180. var
  181.   Count: integer;
  182.  
  183. begin
  184.   gotoxy(LogField[0], LogField[1]);
  185.   for Count := LogField[0] to 79 do write(' ');
  186.   gotoxy(LogField[0], LogField[1]);
  187.   write(Text);
  188. end;
  189.  
  190. procedure WriteWarning(Text: WorkString);
  191.  
  192. var
  193.   Count: integer;
  194.  
  195. begin
  196.   gotoxy(WarningField[0], WarningField[1]);
  197.   for Count := WarningField[0] to 79 do write(' ');
  198.   gotoxy(WarningField[0], WarningField[1]);
  199.   write(Text);
  200. end;
  201.  
  202. procedure WriteError(Text: WorkString);
  203.  
  204. var
  205.   Count: integer;
  206.  
  207. begin
  208.   gotoxy(ErrorField[0], ErrorField[1]);
  209.   for Count := ErrorField[0] to 79 do write(' ');
  210.   gotoxy(ErrorField[0], ErrorField[1]);
  211.   write(Text);
  212. end;
  213.  
  214. procedure WriteDisaster(Text: WorkString);
  215.  
  216. var
  217.   Count: integer;
  218.  
  219. begin
  220.   gotoxy(DisasterField[0], DisasterField[1]);
  221.   for Count := DisasterField[0] to 79 do write(' ');
  222.   gotoxy(DisasterField[0], DisasterField[1]);
  223.   write(Text);
  224. end;
  225.  
  226. procedure GetInput(var Instr: char);
  227.  
  228. var
  229.   Count: integer;
  230.  
  231. begin
  232.   gotoxy(InputField[0], InputField[1]);
  233.   for Count := InputField[0] to 79 do write(' ');
  234.   gotoxy(InputField[0], InputField[1]);
  235.   Beep;
  236.   readln(Instr);
  237. end;
  238.  
  239. procedure GetInformation;
  240.  
  241. { Ask DOS for information about the specified or default disk.
  242.   If we have an error return code from DOS we assume that the disk
  243.   specified was invalid. }
  244.  
  245. var
  246.  
  247.   ValidDrive:  boolean;
  248.   InLetter:    char;
  249.   Instr:       char;
  250.   x:           integer;
  251.  
  252. begin
  253. { get current disk: MS-DOS function call 19h
  254.   information is returned in AL: 0 = A, 1 = B, etc                     }
  255.   WriteLog('Reading Disk Information');
  256.   Registers.ah := $19;
  257.   msdos(Registers);
  258.   DefaultDrive := Registers.al;
  259.   if paramcount = 0
  260.   then
  261.      Instr   := chr(65 + DefaultDrive)
  262.   else
  263.      Instr   := copy(paramstr(1), 1, 1);
  264.   ValidDrive   := false;
  265.   BigFAT       := false;
  266.   with Registers do repeat
  267.        if ord(Instr) < 64 then Instr := chr($FF);
  268.        DriveLetter := upcase(Instr);
  269.        DriveNumber := ord(DriveLetter) - 64;
  270.        ah := $36;
  271.        dl := DriveNumber;
  272.        msdos(Registers);
  273.        if ax <> $ffff
  274.        then begin
  275.             DriveNumber          := DriveNumber - 1;
  276.             FreeClusters         := bx;
  277.             TotalDataClusters    := dx;
  278.             if TotalDataClusters > 4095 then BigFAT := true;
  279.             Sectorsize           := cx;
  280.             ClusterSize          := ax;
  281.             FirstFATsector       := 1;
  282.             if BigFAT then
  283.                begin
  284.                x                  := TotalDataClusters - 4096;
  285.                Count              := (( x + 2 ) * 4 );
  286.                end
  287.             else
  288.                Count              := (( TotalDataClusters + 2 ) * 3 );
  289.             If Count mod ( SectorSize * 2 ) = 0
  290.             then FATsize         := Count div ( SectorSize * 2 )
  291.             else FATsize         := Count div ( SectorSize * 2 ) + 1;
  292.             If BigFAT then FATsize := FATsize + (4096 div (SectorSize div 2));
  293.             FirstDirectorySector := 2 * FATsize + 1;
  294.             ValidDrive           := true;
  295.             if BigFAT then
  296.                  begin
  297.                  ReservedMinimum      := $7FF0;
  298.                  ReservedMaximum      := $7FF6;
  299.                  BadCluster           := $7FF7;
  300.                  LastMinimum          := $7FF8;
  301.                  LastMaximum          := $7FFF;
  302.                  LastNormal           := $7FFF;
  303.                  end
  304.             else
  305.                  begin
  306.                  ReservedMinimum      := $0FF0;
  307.                  ReservedMaximum      := $0FF6;
  308.                  BadCluster           := $0FF7;
  309.                  LastMinimum          := $0FF8;
  310.                  LastMaximum          := $0FFF;
  311.                  LastNormal           := $0FFF;
  312.                  end;
  313.        end
  314.        else begin
  315.             WriteWarning('Invalid driveletter, enter new letter!');
  316.             GetInput(Instr);
  317.             WriteWarning(' ');
  318.        end;
  319.   until ValidDrive;
  320.   if DriveNumber = DefaultDrive then
  321.      NeedReboot := true
  322.   else
  323.      NeedReboot := false;
  324. end;
  325.  
  326. function CarryFlag: boolean;
  327.  
  328. begin
  329.   CarryFlag := ( Registers.Flags and $01 ) <> 0 ;
  330. end;
  331.  
  332. procedure ResetDisk;
  333.  
  334. begin
  335.   Registers.ah := $0D;
  336.   msdos(Registers);
  337. end;
  338.  
  339. procedure ReadSectors(SectorNumber, NumberOfSectors: integer);
  340.  
  341. begin
  342.   with Registers do repeat
  343.        al := DriveNumber;
  344.        cx := NumberOfSectors;
  345.        dx := SectorNumber;
  346.        ds := seg(DTAddress^);
  347.        bx := ofs(DTAddress^);
  348.        int2526($25);
  349.        if CarryFlag then begin
  350.             if not AlreadyWritten
  351.             then begin
  352.                  WriteWarning('No data lost!');
  353.                  WriteError('Disk read error, enter A (abort), R (retry)?');
  354.             end
  355.             else begin
  356.                  WriteError('Probably loss of data!');
  357.                  WriteDisaster('Disk read error A(bort), R(etry), I(gnore)?');
  358.             end;
  359.             Instr := '?';
  360.             repeat
  361.                  Getinput(Instr);
  362.             until ( Instr in ['a', 'A', 'r', 'R'] )
  363.               or (( Instr in ['i', 'I'] ) and AlreadyWritten );
  364.             if Instr in ['a', 'A']
  365.             then begin
  366.                  clrscr;
  367.                  halt;
  368.             end
  369.             else begin
  370.                  WriteError(' ');
  371.                  WriteWarning(' ');
  372.                  WriteDisaster(' ');
  373.                  if Instr in ['i', 'I'] then flags := 0;
  374.        end; end;
  375.   until not CarryFlag;
  376. end;
  377.  
  378. procedure WriteSectors(SectorNumber, NumberOfSectors: integer);
  379.  
  380. begin
  381.   with Registers do repeat
  382.        al := DriveNumber;
  383.        cx := NumberOfSectors;
  384.        dx := SectorNumber;
  385.        ds := seg(DTAddress^);
  386.        bx := ofs(DTAddress^);
  387.        int2526($26);
  388.        if CarryFlag
  389.        then begin
  390.             if not AlreadyWritten
  391.             then begin
  392.                  WriteWarning('No data lost!');
  393.                  WriteError('Disk write error, enter A (abort), R (retry)?');
  394.             end
  395.             else begin
  396.                  WriteError('Probably data lost!');
  397.                  WriteDisaster('Disk write error A(bort), R(etry), I(gnore)?');
  398.             end;
  399.             repeat
  400.                  Getinput(Instr);
  401.             until ( Instr in ['a', 'A', 'r', 'R'] )
  402.               or (( Instr in ['i', 'I'] ) and AlreadyWritten );
  403.             if Instr in ['a', 'A']
  404.             then begin
  405.                  clrscr;
  406.                  halt;
  407.             end
  408.             else begin
  409.                  WriteError(' ');
  410.                  WriteWarning(' ');
  411.                  WriteDisaster(' ');
  412.                  if Instr in ['i', 'I'] then flags := 0;
  413.        end; end;
  414.   until not CarryFlag;
  415.   AlreadyWritten := true;
  416. end;
  417.  
  418. procedure ReadCluster(ClusterNumber: integer);
  419.  
  420. var
  421.   SectorNumber: integer;
  422.  
  423. begin
  424. { To get around Turbo's maxint, (in case of fixed disks of 20 MB the largest
  425.   sectornumber is greater than 32767) we split the following formula:
  426.  
  427.      SectorNumber := ClusterSize * ( ClusterNumber - 2 ) + FirstDataSector;
  428.  
  429.   Multiplication does not return a correct value when Sectornumber becomes
  430.   greater than maxint. Addition returns a word value (16 bits) that is the
  431.   correct sectornumber if interpreted as a non-signed integer.
  432.   Since ClusterSize is ALWAYS (PC-DOS TECH REF: chap Device Drivers,
  433.   boot record layout) a power of 2, we may divide it by 2. }
  434.  
  435.      if ClusterSize < 2
  436.      then SectorNumber := ClusterNumber - 2 + FirstDataSector
  437.      else SectorNumber := ( ClusterSize div 2 ) * ( ClusterNumber - 2 ) +
  438.                           ( ClusterSize div 2 ) * ( ClusterNumber - 2 ) +
  439.                           FirstDataSector;
  440.      ReadSectors(SectorNumber, ClusterSize);
  441. end;
  442.  
  443. procedure WriteCluster(ClusterNumber: integer);
  444.  
  445. var
  446.   SectorNumber: integer;
  447.  
  448. begin
  449. { To get around Turbo's maxint, (in case of fixed disks of 20 MB the largest
  450.   sectornumber is greater than 32767) we split the following formula:
  451.  
  452.      SectorNumber := ClusterSize * ( ClusterNumber - 2 ) + FirstDataSector;
  453.  
  454.   Multiplication does not return a correct value when Sectornumber becomes
  455.   greater than maxint. Addition returns a word value (16 bits) that is the
  456.   correct sectornumber if interpreted as a non-signed integer.
  457.   Since ClusterSize is ALWAYS (PC-DOS TECH REF: chap Device Drivers,
  458.   boot record layout) a power of 2, we may divide it by 2. }
  459.  
  460.      if ClusterSize < 2
  461.      then SectorNumber := ClusterNumber - 2 + FirstDataSector
  462.      else SectorNumber := ( ClusterSize div 2 ) * ( ClusterNumber - 2 ) +
  463.                           ( ClusterSize div 2 ) * ( ClusterNumber - 2 ) +
  464.                           FirstDataSector;
  465.      WriteSectors(SectorNumber, ClusterSize);
  466. end;
  467.  
  468. procedure ReadBootSector(var DTArea: Buffer);
  469.  
  470. { Read the bootsector from disk. Use the information we find in it
  471.   to set a number of variables in the communication block. If the
  472.   information in the bootsector is inconsistent with the story DOS
  473.   told us (GetInformation) we use the FAT identification byte for
  474.   the setting of the variables. This will probably only occur in
  475.   case we have a disk that was formatted under a pre DOS 2.0 version.}
  476.  
  477. var
  478.   FATidentification: byte;
  479.   Instr:             char;
  480.   BootInfo:          Boot absolute DTArea;
  481.  
  482. begin
  483.   WriteLog('Reading Bootsector.');
  484.   ReadSectors(0, 1);
  485.   if        ( TotalDataClusters        >= 16284 )
  486.       or    ( TotalDataClusters        <  0     )
  487.   then begin
  488.      WriteWarning('Disk contains too many clusters for program.');
  489.      WriteError('Program limit is 16283 clusters.');
  490.      WriteDisaster('Press enter to return to DOS.');
  491.      GetInput(Instr);
  492.      clrscr;
  493.      halt;
  494.      end;
  495.   if        ( BootInfo.SectorSize      <> SectorSize )
  496.      or     ( BootInfo.ClusterSize     <> Clustersize )
  497.      or     ( BootInfo.NumberOfFats    =  0 )
  498.      or     ( BootInfo.RootDirSize     =  0 )
  499.      or     ( BootInfo.TotalSectors    <  TotalDataClusters * ClusterSize )
  500.      or not ( BootInfo.MediaDescriptor in [$F0..$FF] )
  501.      or     ( BootInfo.FATsize         <> FATsize )
  502.   then begin
  503.      WriteWarning('Pre DOS 2.0 formatted disk, or incomplete bootsector.');
  504.      ReadSectors(FirstFATsector, 1);
  505.      FATidentification := DTArea[0];
  506.      NumberOfFATs      := 2;
  507.      if ( FATidentification = Single8Sector )
  508.      or ( FATidentification = Single9Sector )
  509.      then RootDirSize  := 64
  510.      { Not Single Sided }
  511.      else if ( FATidentification = Dual8Sector )
  512.           or ( FATidentification = Dual9Sector )
  513.           then RootDirSize  := 112
  514.           else if FATidentification = FixedDisk
  515.                { Fixed Disk }
  516.                then begin
  517.                     WriteError('Fixed Disk: cannot compute size.');
  518.                     WriteDisaster('Press enter to return to DOS.');
  519.                     GetInput(Instr);
  520.                     clrscr;
  521.                     halt;
  522.                end
  523.                else begin
  524.                     WriteError('Unknown Disk Type (FAT id byte).');
  525.                     WriteDisaster('Press enter to return to DOS.');
  526.                     GetInput(Instr);
  527.                     clrscr;
  528.                     halt;
  529.                end;
  530.      FirstDataSector   := NumberOfFats * Fatsize +
  531.                           RootDirSize  * 32 div SectorSize + 1;
  532.      Media             := FATidentification;
  533.  
  534.   end
  535.   else begin
  536.        NumberOfFats    := BootInfo.NumberOfFats;
  537.        if NumberOfFats <> 2
  538.        then FirstDirectorySector := FATsize * NumberOfFats + 1;
  539.        RootDirSize     := BootInfo.RootDirSize;
  540.        FirstDataSector := NumberOfFats * Fatsize +
  541.                           RootDirSize  * 32 div SectorSize + 1;
  542.        Media           := BootInfo.MediaDescriptor;
  543.   end;
  544. end;
  545.  
  546. procedure ReadFat(var UnscrambledFAT: IntArray; var ScrambledFAT: Buffer);
  547.  
  548. { Read and unscramble the FAT. Only the first FAT is processed.}
  549.  
  550. var
  551.   i, Temp:     integer;
  552.  
  553. begin
  554.   WriteLog('Reading and unscrambling FAT.');
  555.   ReadSectors(FirstFATsector, FATsize);
  556.   for i := 0 to TotalDataClusters + 1 do begin
  557.       if BigFAT then
  558.            begin
  559.            move( ScrambledFAT[i * 2], Temp, 2);
  560.            temp := temp and $7FFF;
  561.            unscrambledFAT[i] := Temp;
  562.            end
  563.       else
  564.            begin
  565.            move( ScrambledFAT[3 * i div 2], Temp, 2);
  566.            if odd(i) then Temp := Temp shr 4 else Temp := Temp and $0FFF;
  567.            UnscrambledFAT[i] := Temp;
  568.            end;
  569.   end;
  570. end;
  571.  
  572. procedure WriteFat(var UnscrambledFAT: IntArray; var ScrambledFAT: Buffer);
  573.  
  574. { Write the FAT back to the disk. The FAT has to be scrambled before
  575.   writing. FAT entries on disk are 12 bits long. Because there are mostly
  576.   2 versions of the fat on disk, we write both fats simultaneously.}
  577.  
  578. var
  579.   i,
  580.   Temp1,
  581.   Temp2:          integer;
  582.  
  583. begin
  584.   WriteLog('Writing FAT.');
  585.   for i := 0 to TotalDataClusters + 1 do begin
  586.       if BigFAT then
  587.            begin
  588.            Temp1 := UnscrambledFAT[i];
  589.            if (Temp1 and $4000) <> 0 then Temp1 := Temp1 or $8000;
  590.            move( Temp1, ScrambledFAT[i * 2], 2);
  591.            end
  592.       else
  593.            begin
  594.            Temp1 := UnscrambledFAT[i];
  595.            move( ScrambledFAT[3 * i div 2], Temp2, 2);
  596.            if odd(i) then Temp1 := (Temp2 and $000F) or (Temp1 shl 4)
  597.                 else Temp1 := (Temp2 and $F000) or Temp1;
  598.            move( Temp1, ScrambledFAT[3 * i div 2], 2);
  599.            end;
  600.   end;
  601.   WriteSectors(FirstFATsector, FATsize);
  602.   WriteSectors(FirstFATsector + FATsize, FATsize);
  603. end;
  604.  
  605. procedure ReadSubdirectory(var DTArea:      Buffer;
  606.                            var FATarea:     INTArray;
  607.                            var SubRoot:     DirectoryPointer;
  608.                            StartingCluster: integer);
  609.  
  610. { Link subdirectory entries in a list. Build a tree (by calling this
  611.   routine recursively) if a subdirectory is found.}
  612.  
  613. var
  614.   ClusterNumber,
  615.   DirIndex:      integer;
  616.   Present:       DirectoryPointer;
  617.   EndSearch:     boolean;
  618.  
  619. begin
  620.   Subdirectories := Subdirectories + 1;
  621.   ClusterNumber  := StartingCluster;
  622.   SubRoot        := nil;
  623.   EndSearch      := false;
  624.   repeat
  625.        ReadCluster(ClusterNumber);
  626.        DirIndex     := 0;
  627.        repeat
  628.             if not ( DTArea[DirIndex] in [NeverUsed, Erased] )
  629.             then begin
  630.                  if SubRoot = nil
  631.                  then begin
  632.                       new(SubRoot);
  633.                       Present := SubRoot;
  634.                  end
  635.                  else begin
  636.                       new(Present^.Next);
  637.                       Present := Present^.Next;
  638.                  end;
  639.                  move(DTArea[DirIndex], Present^, 32);
  640.                  if ( Present^.Attribute = Subdirectory ) and
  641.                     ( Present^.EntryName[0] <> '.' )
  642.                  then begin
  643.                       ReadSubdirectory(DTArea, FATarea, Present^.SubDirectory,
  644.                                        Present^.StartingCluster);
  645.                       Readcluster(ClusterNumber);
  646.                  end
  647.                  else begin
  648.                       Present^.SubDirectory := nil;
  649.                       if Present^.Entryname[0] <> '.'
  650.                       then begin
  651.                            TotalFiles            := TotalFiles + 1;
  652.                            InSubdirectories      := InSubdirectories + 1;
  653.                            if ( Present^.Attribute and HiddenFile ) <> 0
  654.                            then HiddenFiles := HiddenFiles + 1;
  655.             end; end; end
  656.             else if DTArea[DirIndex] = NeverUsed
  657.             then EndSearch := true;
  658.             DirIndex := DirIndex + 32;
  659.        until    ( DirIndex >= SectorSize * ClusterSize)
  660.              or (  EndSearch                          );
  661.        ClusterNumber :=  FATarea[ClusterNumber];
  662.   until ( ClusterNumber >= ReservedMinimum ) or EndSearch;
  663.   if Present <> nil then Present^.Next := nil;
  664. end;
  665.  
  666. procedure ReadDirectories(var DTArea: Buffer);
  667.  
  668. { Read the Rootdirectory and whenever an entry for a subdirectory is
  669.   found call ReadSubdirectory. Link all directory entries dynamically
  670.   in a linked list. This list is actually a tree, because the lists
  671.   for subdirectories are linked to this list.}
  672.  
  673. var
  674.   EndSearch:     boolean;
  675.   SectorNumber,
  676.   DirIndex:      integer;
  677.   Present:       DirectoryPointer;
  678.  
  679. begin
  680.   WriteLog('Reading Directory and Subdirectories.');
  681.   SectorNumber := FirstDirectorySector;
  682.   RootDir      := nil;
  683.   EndSearch    := false;
  684.   repeat
  685.        DirIndex := 0;
  686.        ReadSectors(SectorNumber, 1);
  687.        repeat
  688.             if not ( DTArea[DirIndex] in [NeverUsed, Erased] )
  689.             then begin
  690.                  if RootDir = nil
  691.                  then begin
  692.                       new(RootDir);
  693.                       Present := RootDir;
  694.                  end
  695.                  else begin
  696.                       new(Present^.Next);
  697.                       Present := Present^.Next;
  698.                  end;
  699.                  move(DTArea[DirIndex], Present^, 32);
  700.                  if ( Present^.Attribute = Subdirectory ) and
  701.                     ( Present^.EntryName[0] <> '.' )
  702.                  then begin
  703.                       ReadSubdirectory(DTArea, OldFATaddress^,
  704.                                        Present^.SubDirectory,
  705.                                        Present^.StartingCluster);
  706.                       ReadSectors(SectorNumber, 1);
  707.                  end
  708.                  else begin
  709.                       Present^.SubDirectory := nil;
  710.                       if ( Present^.Attribute    <> VolumeLabel ) and
  711.                          ( Present^.Entryname[0] <> '.'         )
  712.                       then begin
  713.                            TotalFiles            := TotalFiles + 1;
  714.                            InRootDirectory       := InRootDirectory + 1;
  715.                            if ( Present^.Attribute and HiddenFile ) <> 0
  716.                            then HiddenFiles := HiddenFiles + 1;
  717.             end; end; end
  718.             else if DTArea[DirIndex] = NeverUsed
  719.             then EndSearch := true;
  720.             DirIndex := DirIndex + 32;
  721.         until ( DirIndex >= SectorSize ) or EndSearch;
  722.         SectorNumber := SectorNumber + 1;
  723.    until ( SectorNumber = FirstDataSector ) or EndSearch;
  724.    if Present <> nil then Present^.Next := nil;
  725. end;
  726.  
  727. procedure RemakeFAT(var OldFATarea, NewFATarea, Permutation: IntArray;
  728.                     Root: DirectoryPointer; Parent, ThisDir: integer);
  729.  
  730. { This procedure is called recursively.
  731.   From the OldFAT and the directory entries we construct a NewFAT and
  732.   a Permutation. The Permutation is used by DoIt for moving the
  733.   clusters. This routine is called one extra time for the chain of
  734.   the empty clusters by LinkFreeDataClusters.
  735.   Recursion is used whenever we find an entry for a subdirectory, in
  736.   the following way: first call this routine for the remainder of the
  737.   current directory, second for the subdirectory.
  738.   The function NewFATindex is used to prevent accidental use of clusters
  739.   that were marked as bad or reserved clusters.}
  740.  
  741. function NextFATindex: integer;
  742.  
  743. var
  744.   Temp: integer;
  745.  
  746. begin
  747.      Temp := NewFATindex + 1;
  748.      while ( OldFATarea[Temp] >= ReservedMinimum ) and
  749.            ( OldFATarea[Temp] <= BadCluster      ) and
  750.            ( Temp <= TotalDataClusters + 1       )
  751.      do begin
  752.           NewFATarea[Temp] := OldFATarea[Temp];
  753.           Temp             := Temp + 1;
  754.      end;
  755.      NextFATindex := Temp;
  756. end;
  757.  
  758. var
  759.   Present: DirectoryPointer;
  760.   Split:   boolean;
  761.   Temp:    integer;
  762.  
  763. begin
  764.   if NewFATindex = 1 then NewFATindex := NextFatindex;
  765.   Present := Root;
  766.   Split   := false;
  767.   while ( Present <> nil ) and not Split do begin
  768.        if ( Present^.Attribute <> VolumeLabel ) and
  769.           ( Present^.StartingCluster <> 0 ) and
  770.           ( Present^.Entryname[0] <> '.')
  771.        then begin
  772.             if Present^.SubDirectory <> nil
  773.             then begin
  774.                  Split := true;
  775.                  RemakeFAT(OldFATarea, NewFATarea, Permutation,
  776.                            Present^.Next, Parent, ThisDir);
  777.             end;
  778.             OldFATindex                 := Present^.StartingCluster;
  779.             Present^.NewStartingCluster := NewFatindex;
  780.             Permutation[NewFATindex]    := OldFATindex;
  781.             while OldFATarea[OldFATindex] < LastMinimum do begin
  782.                   Temp                     := NextFatindex;
  783.                   NewFATarea[NewFATindex]  := Temp;
  784.                   NewFatindex              := Temp;
  785.                   OldFATindex              := OldFATarea[OldFATindex];
  786.                   Permutation[NewFATindex] := OldFATindex;
  787.             end;
  788.             NewFatArea[NewFATindex] := LastNormal;
  789.             NewFATindex             := NextFatindex;
  790.             if Split then
  791.             RemakeFAT(OldFATarea, NewFATarea, Permutation,
  792.                       Present^.SubDirectory, ThisDir,
  793.                       Present^.NewStartingCluster);
  794.        end
  795.        else begin
  796.             if ( Present^.EntryName[0] = '.'  ) and
  797.                ( Present^.EntryName[1] = '.'  )
  798.             then Present^.NewStartingCluster := Parent
  799.             else if Present^.EntryName[0] = '.'
  800.                  then Present^.NewStartingCluster := ThisDir
  801.                  else begin
  802.                       Present^.NewStartingCluster := 0;
  803.                       if Present^.Attribute = VolumeLabel
  804.                       then for Count := 0 to 10 do
  805.                            DiskLabel[Count] := Present^.EntryName[Count];
  806.        end;      end;
  807.        Present := Present^.Next;
  808.   end;
  809. end;
  810.  
  811. procedure LinkFreeClusters(var OldFATarea, NewFATarea: IntArray);
  812.  
  813. { Link Free clusters in a chain, pointed to by Empty^.
  814.   Use RemakeFAT to fill Permutation, but clean NewFAT after
  815.   this. This procedure will ensure that permutation is a
  816.   proper permutation, without double entries which might
  817.   cause DoIt to loop indefinitely or destroy our disk. }
  818.  
  819. var
  820.   Count,
  821.   Next,
  822.   Previous: integer;
  823.   Empty:    DirectoryPointer;
  824.  
  825. begin
  826.   new(Empty);
  827.   Empty^.Next            := nil;
  828.   Empty^.SubDirectory    := nil;
  829.   Empty^.Entryname[0]    := 'X';
  830.   Empty^.Attribute       := HiddenFile;
  831.   Empty^.StartingCluster := 0;
  832.   Count                  := 2;
  833.   while ( Count <= TotalDataClusters + 1 ) and
  834.         ( OldFATarea[Count] <> 0         )
  835.   do Count := Count + 1;
  836.   if Count <= TotalDataClusters + 1
  837.   then begin
  838.        Empty^.StartingCluster := Count;
  839.        Previous               := Count;
  840.        while Count < TotalDataClusters + 1
  841.        do begin
  842.             Count := Count + 1;
  843.             if OldFATarea[Count] = 0
  844.             then begin
  845.                  OldFATarea[Previous] := Count;
  846.                  Previous             := Count;
  847.        end; end;
  848.        OldFATarea[Previous] := LastNormal;
  849.   end;
  850.   if Empty^.StartingCluster <> 0
  851.   then begin
  852.        RemakeFAT(OldFATarea, NewFATarea,
  853.                  PermutationAddress^, Empty, 0, 0);
  854.        Next := Empty^.NewStartingCluster;
  855.        while Next <> LastNormal
  856.        do begin
  857.             Previous             := Next;
  858.             Next                 := NewFATarea[Previous];
  859.             NewFatarea[Previous] := 0;
  860.   end; end;
  861. end;
  862.  
  863. procedure  WriteSubdirectory(var DTArea: Buffer; var OldFATarea: IntArray;
  864.                              Root: DirectoryPointer; Start: integer);
  865.  
  866. { Write subdirectories back to disk. Erased entries are removed
  867.   from the subdirectories. The subdirectories are written to their
  868.   old locations, because DoIt will take care of moving the clusters
  869.   to their new places. No effort is done to truncate a subdirectory
  870.   which would be longer than needed after removal of erased entries.
  871.   We will however set all remaining entries to 'NeverUsed'.
  872.   This routine is used recursively.}
  873.  
  874. var
  875.   Start1,
  876.   ClusterNumber,
  877.   DirIndex:      integer;
  878.   Present:       DirectoryPointer;
  879.  
  880. begin
  881.   Present       := Root;
  882.   ClusterNumber := Start;
  883.   while Present <> nil
  884.   do begin
  885.        DirIndex := 0;
  886.        fillchar(DTArea, ClusterSize * SectorSize, $00);
  887.        repeat
  888.             Start1 := Present^.StartingCluster;
  889.             Present^.StartingCluster := Present^.NewStartingCluster;
  890.             move(Present^, DTArea[DirIndex], 32);
  891.             if ( Present^.Attribute = SubDirectory ) and
  892.                ( Present^.EntryName[0] <> '.' )
  893.             then begin
  894.                  WriteCluster(ClusterNumber);
  895.                  WriteSubdirectory(DTArea, OldFATarea,
  896.                                    Present^.SubDirectory, Start1);
  897.                  ReadCluster(ClusterNumber);
  898.             end;
  899.             Present  := Present^.Next;
  900.             DirIndex := DirIndex + 32;
  901.        until ( DirIndex >= ClusterSize * SectorSize ) or ( Present = nil );
  902.        WriteCluster(ClusterNumber);
  903.        ClusterNumber := OldFATarea[ClusterNumber];
  904.   end;
  905.   if ClusterNumber < LastMinimum
  906.   then begin
  907.        fillchar(DTArea, SectorSize * ClusterSize, $00);
  908.        while ClusterNumber < LastMinimum
  909.        do begin
  910.             WriteCluster(ClusterNumber);
  911.             ClusterNumber := OldFATarea[ClusterNumber];
  912.   end; end;
  913. end;
  914.  
  915. procedure WriteDirectories(var DTArea: Buffer);
  916.  
  917. { Write rootdirectory back to disk. Erased entries are removed
  918.   from the directory. When we find a subdirectory entry, we first
  919.   process this subdirectory by calling WriteSubdirectories,
  920.   before we proceed with the root. All entries that are no in use
  921.   are set to 'NeverUsed'.}
  922.  
  923. var
  924.   Start,
  925.   SectorNumber,
  926.   DirIndex:      integer;
  927.   Present:       DirectoryPointer;
  928.  
  929. begin
  930.   WriteLog('Writing new Directory and Subdirectories.');
  931.   SectorNumber := FirstDirectorySector;
  932.   Present      := RootDir;
  933.   while Present <> nil
  934.   do begin
  935.        DirIndex := 0;
  936.        fillchar(DTArea, SectorSize, $00);
  937.        repeat
  938.             Start := Present^.StartingCluster;
  939.             Present^.StartingCluster := Present^.NewStartingCluster;
  940.             move(Present^, DTArea[DirIndex], 32);
  941.             if ( Present^.Attribute = SubDirectory ) and
  942.                ( Present^.EntryName[0] <> '.' )
  943.             then begin
  944.                  WriteSectors(SectorNumber, 1);
  945.                  WriteSubdirectory(DTArea, OldFATaddress^,
  946.                                    Present^.SubDirectory, Start);
  947.                  ReadSectors(SectorNumber, 1);
  948.             end;
  949.             Present  := Present^.Next;
  950.             DirIndex := DirIndex + 32;
  951.        until ( DirIndex >= SectorSize ) or ( Present = nil );
  952.        WriteSectors(SectorNumber, 1);
  953.        SectorNumber := SectorNumber + 1;
  954.   end;
  955.   if SectorNumber < FirstDataSector
  956.   then begin
  957.        fillchar(DTArea, SectorSize, $00);
  958.        while SectorNumber < FirstDataSector
  959.        do begin
  960.             WriteSectors(SectorNumber, 1);
  961.             SectorNumber := SectorNumber + 1;
  962.   end; end;
  963. end;
  964.  
  965. procedure DoIt(var Permutation: IntArray; var DTArea, SaveArea: Buffer);
  966.  
  967. { DoIt. This routine performs the actual reformating of the disk.
  968.   The array Permutation contains in every location  [i] (starting
  969.   from 2) which cluster has to be moved to cluster location i.
  970.   Because we have a real permutation, this permutation can be
  971.   parsed into a number of cyclical permutations. We start at the
  972.   first cyclic permutation that is not identity. We save the first
  973.   cluster of this cyclical permutation, proceed through the cyclical
  974.   permutation, moving one cluster at a time, until we finish the
  975.   cycle. We than write the saved cluster to disk.}
  976.  
  977. var
  978.   Prior,
  979.   Next,
  980.   LastStart: integer;
  981.  
  982. begin
  983.  
  984.   WriteLog('Reformatting......');
  985.   LastStart := 2;
  986.   while LastStart <= TotalDataClusters + 1
  987.   do begin
  988.        if LastStart = Permutation[LastStart]
  989.        then LastStart := LastStart + 1
  990.        else begin
  991.             ReadCluster(LastStart);
  992.             move(DTArea, SaveArea, SectorSize * ClusterSize);
  993.             Prior := LastStart;
  994.             Next  := Permutation[LastStart];
  995.             repeat
  996.                  ReadCluster(Next);
  997.                  WriteCluster(Prior);
  998.                  MovedClusters      := MovedClusters + 1;
  999.                  gotoxy(MovedField[0], MovedField[1]);
  1000.                  write(MovedClusters:10);
  1001.                  Permutation[Prior] := Prior;
  1002.                  Prior              := Next;
  1003.                  Next               := Permutation[Next];
  1004.             until Next = LastStart;
  1005.             move(SaveArea, DTArea, SectorSize * ClusterSize);
  1006.             WriteCluster(Prior);
  1007.             MovedClusters      := MovedClusters + 1;
  1008.             gotoxy(MovedField[0], MovedField[1]);
  1009.             write(MovedClusters:10);
  1010.             Permutation[Prior] := Prior;
  1011.   end; end;
  1012.   WriteLog(' ');
  1013. end;
  1014.  
  1015. procedure InitScreen;
  1016.  
  1017. var
  1018.   Row,
  1019.   Column: integer;
  1020. begin
  1021.   normvideo;
  1022.   clrscr;
  1023.   Row := 2;
  1024.   write(char(201)); for Column := 2 to 79 do write(char(205));
  1025.   write(char(187));
  1026.   write(char(186)); gotoxy(80, Row);
  1027.   write(char(186));
  1028.   gotoxy(15, Row); write('REFORMAT: an original JOS disk tool. Ver: 1.21(mod)');
  1029.   Row := Row + 1; gotoxy(1, Row);
  1030.   write(char(199)); for Column := 2 to 79 do write(char(196));
  1031.   write(char(182));
  1032.   for Row := 4 to 15 do
  1033.   begin
  1034.   write(char(186)); gotoxy(80, Row);
  1035.   write(char(186));
  1036.   end;
  1037.   write(char(199)); for Column := 2 to 79 do write(char(196));
  1038.   write(char(182));
  1039.   write(char(186)); gotoxy(80, 17);
  1040.   write(char(186));
  1041.   write(char(199)); for Column := 2 to 79 do write(char(196));
  1042.   write(char(182));
  1043.   for Row := 19 to 23 do
  1044.   begin
  1045.   write(char(186)); gotoxy(80, Row);
  1046.   write(char(186));
  1047.   end;
  1048.   write(char(200)); for Column := 2 to 79 do write(char(205));
  1049.   write(char(188));
  1050.   gotoxy(05, 19); write('User Input Field :');
  1051.   gotoxy(05, 20); write('Activity Logging :');
  1052.   gotoxy(05, 21); write('Warning  Messages:');
  1053.   gotoxy(05, 22); write('Error    Messages:');
  1054.   gotoxy(05, 23); write('Disaster Messages:');
  1055.   InputField[0]    := 24;
  1056.   InputField[1]    := 19;
  1057.   LogField[0]      := 24;
  1058.   LogField[1]      := 20;
  1059.   WarningField[0]  := 24;
  1060.   WarningField[1]  := 21;
  1061.   ErrorField[0]    := 24;
  1062.   ErrorField[1]    := 22;
  1063.   DisasterField[0] := 24;
  1064.   DisasterField[1] := 23;
  1065. end;
  1066.  
  1067. procedure CheckSubdirectory(var FAT: IntArray;
  1068.                             Root: DirectoryPointer; Parent, ThisDir: integer);
  1069.  
  1070. { This procedure is called recursively.
  1071.   The SubDirectories are checked here. No attempt is made
  1072.   to correct any errors found. If any errors are found, a message
  1073.   is issued and the program stops. The users must first run CHKDSK from
  1074.   DOS before we accept the disk.                                        }
  1075.  
  1076. var
  1077.   Present: DirectoryPointer;
  1078.   Prior,
  1079.   Next:    integer;
  1080.  
  1081. begin
  1082.   Present := Root;
  1083.   while ( Present <> nil ) and ( Errors = 0 ) begin
  1084.        if ( Present^.Attribute <> VolumeLabel ) and
  1085.           ( Present^.StartingCluster <> 0 ) and
  1086.           ( Present^.Entryname[0] <> '.')
  1087.        then begin
  1088.             Next  := Present^.StartingCluster;
  1089.             Count := 0;
  1090.             repeat;
  1091.                  if ( Next > TotalDataClusters + 1 )
  1092.                  or ( Next < 1                     )
  1093.                  then begin
  1094.                       Errors := Errors + 1;
  1095.                       end
  1096.                  else begin
  1097.                       Prior      := Next;
  1098.                       Next       := FAT[Prior];
  1099.                       FAT[Prior] := 0;
  1100.                       if Next <> Prior + 1 then Count := Count + 1;
  1101.                  end;
  1102.             until ( Next >= LastMinimum ) or ( Errors <> 0 );
  1103.             if Count > 1 then NonContiguousFiles := NonContiguousFiles + 1;
  1104.             if Present^.SubDirectory <> nil
  1105.             then CheckSubdirectory(FAT, Present^.SubDirectory,
  1106.                                    ThisDir, Present^.StartingCluster);
  1107.        end
  1108.        else begin
  1109.             if ( Present^.EntryName[0] = '.' ) and
  1110.                ( Present^.EntryName[1] = '.' )
  1111.             then if Present^.StartingCluster <> Parent
  1112.                  then Errors := Errors + 1
  1113.                  else
  1114.             else if Present^.EntryName[0] = '.'
  1115.                  then if Present^.StartingCluster <> ThisDir
  1116.                       then Errors := Errors + 1
  1117.                       else
  1118.                  else if Present^.StartingCluster <> 0
  1119.                       then Errors := Errors + 1;
  1120.        end;
  1121.        Present := Present^.Next;
  1122.   end;
  1123. end;
  1124.  
  1125. procedure CheckDisk(var FAT: IntArray; Root: DirectoryPointer);
  1126.  
  1127. { The FAT and the Directories are checked here. No attempt is made
  1128.   to correct any errors found. If any errors are found, a message
  1129.   is issued and the program stops. The users must first run CHKDSK from
  1130.   DOS before we accept the disk.                                        }
  1131.  
  1132. begin
  1133.   WriteLog('Checking FAT....');
  1134.   CheckSubdirectory(FAT, Root, 0, 0);
  1135.   for Count := 2 to TotalDataClusters + 1 do
  1136.        if ( FAT[Count] <> 0 ) and
  1137.           ( ( FAT[Count] < ReservedMinimum ) or
  1138.             ( FAT[Count] > BadCluster      ) )
  1139.        then LostClusters := LostClusters + 1;
  1140.   if Errors <> 0
  1141.   then begin
  1142.        WriteError('Crosslinked clusters found. Run CHKDSK first.');
  1143.        WriteWarning('Press Enter to return to DOS.');
  1144.        GetInput(Instr);
  1145.        clrscr;
  1146.        halt;
  1147.   end
  1148.   else if LostClusters <> 0
  1149.   then begin
  1150.        WriteError('Lost clusters found. Run CHKDSK first.');
  1151.        WriteWarning('Press Enter to return to DOS.');
  1152.        GetInput(Instr);
  1153.        clrscr;
  1154.        halt;
  1155.   end;
  1156. end;
  1157.  
  1158. procedure CountClustersToMove(var Permutation: IntArray);
  1159.  
  1160. begin
  1161.   for Count := 2 to TotalDataClusters + 1
  1162.   do if Permutation[Count] <> Count then ClustersToMove := ClustersToMove + 1;
  1163. end;
  1164.  
  1165. procedure InitCounters;
  1166.  
  1167. begin
  1168.   OldFATindex         := 0;
  1169.   NewFATindex         := 1;
  1170.   Errors              := 0;
  1171.   LostClusters        := 0;
  1172.   TotalFiles          := 0;
  1173.   HiddenFiles         := 0;
  1174.   InRootDirectory     := 0;
  1175.   InSubdirectories    := 0;
  1176.   NonContiguousFiles  := 0;
  1177.   Subdirectories      := 0;
  1178.   MovedClusters       := 0;
  1179.   ClustersToMove      := 0;
  1180.   Count               := 0;
  1181.   AlreadyWritten      := false;
  1182.   DiskLabel           := '           ';
  1183. end;
  1184.  
  1185. procedure WriteStatistics;
  1186.  
  1187. var
  1188.   Row: integer;
  1189.  
  1190. begin
  1191.   if NonContiguousFiles = 0 then ClustersToMove := 0;
  1192.   Row := 5;
  1193.   if DiskLabel <> '           '
  1194.   then begin
  1195.        gotoxy(18, Row); write('Volume Label is . . . . . :   ', DiskLabel);
  1196.        Row := Row + 1;
  1197.   end;
  1198.   gotoxy(18, Row); write(     'Total # of files. . . . . :', TotalFiles:10);
  1199.   if HiddenFiles <> 0
  1200.   then             write(' (hidden:', HiddenFiles:3,')');
  1201.   Row := Row + 1;
  1202.   if Subdirectories = 0
  1203.   then begin
  1204.        gotoxy(18, Row); write('All files in Rootdirectory.');
  1205.   end
  1206.   else begin
  1207.        gotoxy(18, Row); write('  in Root directory . . . :',
  1208.                                  InRootDirectory:10);
  1209.        Row := Row + 1;
  1210.        gotoxy(18, Row); write('  in ', Subdirectories:3, ' Subdirectories . :',
  1211.                                  InSubDirectories:10);
  1212.   end;
  1213.   Row := Row + 1;
  1214.   gotoxy(18, Row);      write('# of noncontiguous files. :',
  1215.                                NonContiguousFiles:10);
  1216.   Row := Row + 1;
  1217.   gotoxy(18, Row);      write('# of clusters to be moved :',
  1218.                                ClustersToMove:10);
  1219.   Row := Row + 1;
  1220.   gotoxy(18, Row);      write('# of clusters moved . . . :',
  1221.                                MovedClusters:10);
  1222.   MovedField[0] := 45;
  1223.   MovedField[1] := Row;
  1224.   Row := Row + 2;
  1225.   gotoxy(05, Row);      write('Clustersize . . :', ClusterSize:06,
  1226.                               ' sectors.');
  1227.   gotoxy(45, Row);      write('Sectorsize. . . :', SectorSize:06,
  1228.                               ' bytes.');
  1229.   Row := Row + 1;
  1230.   gotoxy(05, Row);      write('Total data space:', TotalDataClusters:6,
  1231.                               ' clusters.');
  1232.   gotoxy(45, Row);      write('DOS space . . . :', FirstDataSector:6,
  1233.                               ' sectors.');
  1234.   Row := Row + 1;
  1235.   gotoxy(05, Row);      write('Free data space :', FreeClusters:6,
  1236.                               ' clusters.');
  1237.   gotoxy(45, Row);      write('Disk type . . . :');
  1238.   case Media of
  1239.        $F8:   { FixedDisk    }   write(' Fixed Disk');
  1240.        $FE:   { Single8Sector}   write(' 1 sided / 8 sect');
  1241.        $FF:   { Dual8Sector  }   write(' 2 sided / 8 sect');
  1242.        $FC:   { Single9sector}   write(' 1 sided / 9 sect');
  1243.        $FD:   { Dual9sector  }   write(' 2 sided / 9 sect');
  1244.   end;
  1245. end;
  1246.  
  1247. procedure WriteDoc;
  1248.  
  1249. begin
  1250.   clrscr;
  1251.   writeln;
  1252.   writeln('           REFORMAT: an original JOS disk tool.');
  1253.   writeln;
  1254.   writeln('                Public Domain Software.');
  1255.   writeln;
  1256.   writeln('Makes all files  on a floppy or  fixed disk contiguous  again,');
  1257.   writeln('improving  disk  performance  dramatically. Either fixed disks');
  1258.   writeln('or diskettes.                               Requires DOS 2.xx.');
  1259.   writeln('Register at the following address to be on my mailing list for');
  1260.   writeln('updates:');
  1261.   writeln;
  1262.   writeln('                   Jos Wennmacker');
  1263.   writeln('                   Universitair Rekencentrum');
  1264.   writeln('                   Geert Grooteplein Zuid 41');
  1265.   writeln('                   NL-6525 GA Nijmegen');
  1266.   writeln('                   The Netherlands');
  1267.   writeln;
  1268.   writeln;
  1269.   writeln;
  1270.   writeln('Also comments, bugs etc are expected at one of these addresses.');
  1271.   writeln;
  1272.   writeln('       Press enter to see next page');
  1273.   readln;
  1274.   clrscr;
  1275.   writeln;
  1276.   writeln('           REFORMAT: an original JOS disk tool.');
  1277.   writeln;
  1278.   writeln('                Public Domain Software.');
  1279.   writeln;
  1280.   writeln;
  1281.   writeln('Use: Reformat [d:]');
  1282.   writeln;
  1283.   writeln('where d: is an optional driveletter. Ommiting d: will select the');
  1284.   writeln('default  drive.  This  program  works for  both fixed  disks and');
  1285.   writeln('floppies.');
  1286.   writeln;
  1287.   writeln('*  WARNING * WARNING * WARNING * WARNING * WARNING * WARNING  **');
  1288.   writeln;
  1289.   writeln('NEVER use  this  program  on a disk that  contains * PROTECTED *');
  1290.   writeln('software. You might find these  programs turned into an  illegal');
  1291.   writeln('copy or even end up with a scrambled disk!!!!!!');
  1292.   writeln('Always *UNINSTALL* this kind of software before using REFORMAT!!');
  1293.   writeln('The program will  prompt you to confirm  this in case of a fixed');
  1294.   writeln('disk.');
  1295.   writeln;
  1296. end;
  1297.  
  1298. begin
  1299.   if paramcount <> 0
  1300.   then if copy(paramstr(1), 1, 1) = '?'
  1301.        then begin
  1302.             WriteDoc;
  1303.             halt;
  1304.        end
  1305.        else begin
  1306.             if ( paramcount > 1 )
  1307.             or ( length(paramstr(1)) > 2 )
  1308.             or ( (length(paramstr(1)) = 2 ) and
  1309.                  (copy(paramstr(1), 2, 1) <> ':') )
  1310.             then begin
  1311.                  writeln;
  1312.                  writeln('Invalid parameter: REFORMAT [d:] or ?.');
  1313.                  halt;
  1314.        end; end;
  1315.   InitCounters;
  1316.   InitScreen;
  1317.   GetInformation;
  1318.   if ClusterSize < FATsize
  1319.   then getmem(DTAddress, SectorSize * FATsize)
  1320.   else getmem(DTAddress, SectorSize * ClusterSize);
  1321.   getmem(SAVEaddress, SectorSize * ClusterSize);
  1322.   getmem(PermutationAddress, TotalDataClusters * 2 + 4);
  1323.   getmem(OldFATaddress, TotalDataClusters * 2 + 4);
  1324.   getmem(NewFATaddress, TotaldataClusters * 2 + 4);
  1325.   ReadBootSector(DTAddress^);
  1326.   ReadFat(OldFATaddress^, DTAddress^);
  1327.   ReadDirectories(DTAddress^);
  1328.   move(OldFATaddress^, NewFATaddress^, TotalDataClusters * 2 + 4);
  1329.   CheckDisk(NewFATaddress^, RootDir);
  1330.   fillchar(NewFATaddress^, TotalDataClusters * 2 + 4, 0);
  1331.   for Count := 0 to TotalDataClusters + 1 do
  1332.   PermutationAddress^[Count] := Count;
  1333.   move(OldFATaddress^, NewFATaddress^, 4);
  1334.   RemakeFAT(OldFATaddress^, NewFATaddress^,
  1335.             PermutationAddress^, RootDir, 0, 0);
  1336.   LinkFreeClusters(OldFATaddress^, NewFATaddress^);
  1337.   CountClustersToMove(PermutationAddress^);
  1338.   WriteStatistics;
  1339.   if NonContiguousFiles <> 0
  1340.   then begin
  1341.        if Media = FixedDisk
  1342.        then begin
  1343.             gotoxy(05, 17);
  1344.             write ('Fixed disk: did you uninstall all protected software? ',
  1345.                    'Continue (Y/N)?');
  1346.             Instr := 'Q';
  1347.             while not ( Instr in ['Y', 'y', 'N', 'n'] )
  1348.             do GetInput(Instr);
  1349.             if Instr in ['N', 'n']
  1350.             then begin
  1351.                  WriteWarning('Press Enter to return to DOS.');
  1352.                  GetInput(Instr);
  1353.                  clrscr;
  1354.                  halt;
  1355.        end; end;
  1356.        ResetDisk;
  1357.        WriteFAT(NewFATaddress^, DTAddress^);
  1358.        WriteDirectories(DTAddress^);
  1359.        DoIt(PermutationAddress^, DTAddress^, SAVEaddress^);
  1360.        ResetDisk;
  1361.        if NeedReboot then
  1362.           begin
  1363.           repeat
  1364.              begin
  1365.              WriteLog('Done ! Please reboot system to continue');
  1366.              GetInput(Anything);
  1367.              end;
  1368.           until 1 = 2;
  1369.           end
  1370.        else
  1371.           WriteLog('Done ! Press Enter-Key to return to DOS.');
  1372.   end
  1373.   else begin
  1374.        WriteWarning('All files are contiguous. Nothing to be done!');
  1375.        WriteLog('Press Enter-Key to return to DOS.');
  1376.   end;
  1377.   GetInput(Anything);
  1378.   clrscr;
  1379. end.
  1380.